package com.yunkeji.system.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yunkeji.common.core.domain.PageQuery;
import com.yunkeji.common.core.domain.dto.PayLogDTO;
import com.yunkeji.common.core.domain.event.PayLogEvent;
import com.yunkeji.common.core.page.TableDataInfo;
import com.yunkeji.common.enums.app.*;
import com.yunkeji.common.exception.ServiceException;
import com.yunkeji.common.helper.LoginHelper;
import com.yunkeji.common.msg.PushMessage;
import com.yunkeji.common.utils.DateUtils;
import com.yunkeji.common.utils.StringUtils;
import com.yunkeji.dao.domain.*;
import com.yunkeji.dao.mapper.*;
import com.yunkeji.dao.vo.AppUserVo;
import com.yunkeji.dao.vo.PayWalletLogVo;
import com.yunkeji.dao.vo.PhoneLogVo;
import com.yunkeji.dao.vo.broker.IndexVo;
import com.yunkeji.system.domain.bo.*;
import com.yunkeji.system.mapper.AdminUserMapper;
import com.yunkeji.system.service.IAppUserService;
import com.yunkeji.system.service.IBrokerUserService;
import com.yunkeji.system.service.IPhoneLogService;
import com.yunkeji.system.service.ISysUserService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 用户基本信息-扩展(存放一些不重要但是必要的信息)Service业务层处理
 *
 * @author zhang
 * @date 2024-07-10
 */
@RequiredArgsConstructor
@Service
@Transactional
public class AppUserServiceImpl implements IAppUserService {

    private final AdminUserMapper adminUserMapper;
    private final AppUserMapper appUserMapper;
    private final UserBannedMapper userBannedMapper;
    private final AppUserExtMapper appUserExtMapper;
    private final AppUserWalletMapper appUserWalletMapper;
    private final AppUserVipMapper appUserVipMapper;
    private final PayDetailMapper payDetailMapper;
    private final IPhoneLogService phoneLogService;
    private final PayWalletLogMapper walletLogMapper;

    /**
     * 查询用户基本信息-扩展(存放一些不重要但是必要的信息)
     */
    @Override
    public AppUserVo queryById(Long id) {
        AppUserVo appUserVo = appUserMapper.selectVoById(id);
        if (appUserVo.getPid() != null) {
            AppUser user = appUserMapper.selectById(appUserVo.getPid());
            appUserVo.setPid(user.getPlatformId());
        }
        return appUserVo;
    }

    /**
     * 查询用户基本信息-扩展(存放一些不重要但是必要的信息)列表
     */
    @Override
    public TableDataInfo<AppUserVo> queryPageList(AppUserBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<AppUser> lqw = buildQueryWrapper(bo);
        Page<AppUserVo> result = appUserMapper.selectVoPage(pageQuery.build(), lqw);
        List<AppUserVo> records = result.getRecords();
        if (CollectionUtil.isNotEmpty(records)) {
            List<Long> collect =
                records.stream().map(AppUserVo::getId).collect(Collectors.toList());
            List<Long> pids = records.stream().map(AppUserVo::getPid).collect(Collectors.toList());
            Map<Long, Long> pidMap =
                appUserMapper.selectVoBatchIds(pids).stream()
                    .collect(Collectors.toMap(AppUserVo::getId, AppUserVo::getPlatformId));
            Map<Long, AppUserWallet> collect1 =
                appUserWalletMapper.selectBatchIds(collect).stream()
                    .collect(Collectors.toMap(AppUserWallet::getId, item -> item));
            records.forEach(
                item -> {
                    item.setDiamondBalance(collect1.get(item.getId()).getDiamondBalance());
                    item.setGoldBalance(collect1.get(item.getId()).getGoldBalance());
                    item.setPlatformIdParent(pidMap.get(item.getPid()));
                });
        }

        return TableDataInfo.build(result);
    }

    /**
     * 查询用户基本信息-扩展(存放一些不重要但是必要的信息)列表
     */
    @Override
    public List<AppUserVo> queryList(AppUserBo bo) {
        LambdaQueryWrapper<AppUser> lqw = buildQueryWrapper(bo);
        return appUserMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<AppUser> buildQueryWrapper(AppUserBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<AppUser> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getPlatformId() != null, AppUser::getPlatformId, bo.getPlatformId());
        lqw.eq(StringUtils.isNotBlank(bo.getPhone()), AppUser::getPhone, bo.getPhone());
        lqw.eq(bo.getCardAuth() != null, AppUser::getCardAuth, bo.getCardAuth());
        lqw.eq(bo.getVideoAuth() != null, AppUser::getVideoAuth, bo.getVideoAuth());
        lqw.eq(bo.getVideoPrice() != null, AppUser::getVideoPrice, bo.getVideoPrice());
        lqw.eq(bo.getVoicePrice() != null, AppUser::getVoicePrice, bo.getVoicePrice());
        lqw.eq(bo.getStatus() != null, AppUser::getStatus, bo.getStatus());
        lqw.eq(bo.getOnlineStatus() != null, AppUser::getOnlineStatus, bo.getOnlineStatus());
        lqw.eq(bo.getPid() != null, AppUser::getPid, bo.getPid());
        lqw.eq(bo.getLevel() != null, AppUser::getLevel, bo.getLevel());
        lqw.eq(bo.getAccountType() != null, AppUser::getAccountType, bo.getAccountType());
        lqw.eq(bo.getAccountDelDate() != null, AppUser::getAccountDelDate, bo.getAccountDelDate());
        lqw.eq(
            bo.getRecentlyAccessTime() != null,
            AppUser::getRecentlyAccessTime,
            bo.getRecentlyAccessTime());
        lqw.eq(bo.getDisturbFlag() != null, AppUser::getDisturbFlag, bo.getDisturbFlag());
        lqw.eq(bo.getLimitDate() != null, AppUser::getLimitDate, bo.getLimitDate());
        lqw.eq(bo.getSex() != null, AppUser::getSex, bo.getSex());
        lqw.eq(bo.getBranch() != null, AppUser::getBranch, bo.getBranch());
        lqw.orderByDesc(AppUser::getPlatformId);
        return lqw;
    }

    /**
     * 新增用户基本信息-扩展(存放一些不重要但是必要的信息)
     */
    @Override
    public Boolean insertByBo(AppUserBo bo) {
        AppUser add = BeanUtil.toBean(bo, AppUser.class);
        validEntityBeforeSave(add);
        boolean flag = appUserMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }

    /**
     * 修改用户基本信息-扩展(存放一些不重要但是必要的信息)
     */
    @Override
    public Boolean updateByBo(AppUserBo bo) {
        AppUser update = BeanUtil.toBean(bo, AppUser.class);
        validEntityBeforeSave(update);
        return appUserMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(AppUser entity) {
        // TODO 做一些数据校验,如唯一约束
    }

    /**
     * 批量删除用户基本信息-扩展(存放一些不重要但是必要的信息)
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            // TODO 做一些业务上的校验,判断是否需要校验
        }
        return appUserMapper.deleteBatchIds(ids) > 0;
    }

    /**
     * 踢下线
     *
     * @param id
     */
    @Override
    public void kickOnline(Long id) {
        this.kickOnline(id, "用户被踢下线");
    }

    @Override
    public void kickOnline(Long id, String reason) {
        LoginHelper.removeLoginCacheInfo(id);

        AppUser appUser = new AppUser();
        appUser.setOnlineStatus(AppUserOnlineStatus.离线.getCode());
        appUser.setId(id);
        appUserMapper.updateById(appUser);

        PushMessage.kickout(appUser.getPlatformId(), reason);
    }

    /**
     * 封号
     */
    @Override
    public void bannedAccount(UserBannedBo userBannedBo) {
        UserBanned userBanned = BeanUtil.toBean(userBannedBo, UserBanned.class);
        Integer limitType = userBanned.getLimitType();
        DateTime dateTime = DateUtil.offsetDay(new Date(), limitType);
        userBanned.setEndTime(dateTime);
        AppUser appUser = appUserMapper.selectById(userBanned.getUserId());

        LoginHelper.removeLoginCacheInfo(appUser.getId());

        AppUserExt appUserExt = appUserExtMapper.selectById(appUser.getId());
        userBanned.setPlatformId(appUser.getPlatformId());
        userBanned.setHeadUrl(appUserExt.getHeadUrl());
        userBanned.setNickName(appUserExt.getNickName());
        userBanned.setPhoneNum(appUser.getPhone());
        userBannedMapper.insert(userBanned);
        appUser.setStatus(AppUserStatus.封号.getCode());
        appUser.setOnlineStatus(AppUserOnlineStatus.离线.getCode());
        appUser.setLimitDate(dateTime);
        appUserMapper.updateById(appUser);

        PushMessage.kickout(appUser.getPlatformId(), "用户被封号");
    }

    /**
     * 解封
     *
     * @param uid
     */
    @Override
    public void releaseAccount(Long uid) {
        AppUser appUser = new AppUser();
        appUser.setId(uid);
        appUser.setStatus(AppUserStatus.正常.getCode());
        appUserMapper.updateById(appUser);
    }

    @Override
    public void setAccountType(SetAccountTypeBo setAccountTypeBo) {
        AppUser appUser = new AppUser();
        appUser.setId(setAccountTypeBo.getUserId());
        appUser.setOnlineStatus(AppUserOnlineStatus.离线.getCode());
        appUser.setAccountType(setAccountTypeBo.getAccountType());
        appUserMapper.updateById(appUser);
        LoginHelper.removeLoginCacheInfo(setAccountTypeBo.getUserId());
    }

    /**
     * 取消绑定用户
     *
     * @param toBindAccountBo
     */
    @Override
    public void unBindUser(ToBindAccountBo toBindAccountBo) {
        AppUser user1 = appUserMapper.selectById(toBindAccountBo.getId());
        if (user1 == null) {
            throw new ServiceException("取消绑定的用户不存在");
        }
        adminUserMapper.unBindUser(toBindAccountBo.getId());
    }

    /**
     * 绑定用户
     *
     * @param toBindAccountBo
     */
    @Override
    public void bindUser(ToBindAccountBo toBindAccountBo) {
        AppUser user =
            appUserMapper.selectOne(
                Wrappers.lambdaQuery(AppUser.class)
                    .eq(AppUser::getPlatformId, toBindAccountBo.getPlatformId()));
        if (user == null) {
            throw new ServiceException("用户不存在");
        }
        if (user.getPid() != null) {
            throw new ServiceException("用户已绑定,请先取消绑定");
        }
        if (user.getId().equals(toBindAccountBo.getId())) {
            throw new ServiceException("不能绑定自己");
        }

        AppUser user1 = appUserMapper.selectById(toBindAccountBo.getId());
        if (user1 == null) {
            throw new ServiceException("邀请用户不存在");
        }
        AppUser updateUser = new AppUser();
        updateUser.setPid(toBindAccountBo.getId());
        updateUser.setId(user.getId());
        appUserMapper.updateById(updateUser);
    }

    @Override
    public void toSort(ToSortBo toSortBo) {
        AppUser updateUser = new AppUser();
        //默认给排序初始化
        int sort;
        //排序大于0的时候，默认增加3000
        if(toSortBo.getSort() > 0) {
            sort = toSortBo.getSort() + 3000;
        } else {
            //否则直接默认1000的排序
            sort = 1000;
        }
        updateUser.setSort(sort);
        updateUser.setId(toSortBo.getId());
        appUserMapper.updateById(updateUser);
    }

    @Override
    public void addVip(AddVipBo addVipBo) {
        AppUserVip vip = appUserVipMapper.selectById(addVipBo.getId());
        // 获取默认的会员过期时间（从当前时间进行增加天数）
        DateTime expireTime = DateUtil.offsetDay(new Date(), addVipBo.getExpireTime());
        // 判断新增的是普通会员还是专属会员
        boolean isNormalVip =
            AppUserVipType.getByCode(addVipBo.getVipType()) == AppUserVipType.普通会员;
        // 判断是否有会员信息，没会员信息新增
        if (null == vip) {
            vip = BeanUtil.toBean(addVipBo, AppUserVip.class);
            if (isNormalVip) {
                // 普通会员的操作
                vip.setCommonVipFlag(EnableEnum.YES.getCode());
                vip.setCommonVipExpireTime(expireTime);
            } else {
                // 专属会员的操作
                vip.setExclusiveVipFlag(EnableEnum.YES.getCode());
                vip.setExclusiveVipExpireTime(expireTime);
            }
            appUserVipMapper.insert(vip);
            insertVipLog(addVipBo, new Date(), expireTime);
            return;
        }
        Date beforeTime;
        if (isNormalVip) {
            // 普通会员的操作
            // 判断原本是不是会员
            if (vip.getCommonVipFlag().equals(EnableEnum.NO.getCode())) {
                vip.setCommonVipFlag(EnableEnum.YES.getCode());
            } else {
                // 判断过期时间是否小于当前时间，如果小于，则以当前时间为基础
                Date date = DateUtils.getNowDate();
                if (vip.getCommonVipExpireTime().getTime() > System.currentTimeMillis()) {
                    date = vip.getCommonVipExpireTime();
                }
                expireTime =
                    DateUtil.offsetDay(date, addVipBo.getExpireTime());
            }
            beforeTime = vip.getCommonVipExpireTime();
            vip.setCommonVipExpireTime(expireTime);
        } else {
            if (vip.getExclusiveVipFlag().equals(EnableEnum.NO.getCode())) {
                vip.setExclusiveVipFlag(EnableEnum.YES.getCode());
            } else {
                // 判断过期时间是否小于当前时间，如果小于，则以当前时间为基础
                Date date = DateUtils.getNowDate();
                if (vip.getExclusiveVipExpireTime().getTime() > System.currentTimeMillis()) {
                    date = vip.getExclusiveVipExpireTime();
                }
                expireTime =
                    DateUtil.offsetDay(
                        date, addVipBo.getExpireTime());
            }
            beforeTime = vip.getExclusiveVipExpireTime();
            vip.setExclusiveVipExpireTime(expireTime);
        }
        appUserVipMapper.updateById(vip);
        insertVipLog(addVipBo, beforeTime, expireTime);
    }

    @Override
    public void addWallet(AddWalletBo addWalletBo) {
//        if(Objects.isNull(addWalletBo.getHandleType())){
//            throw new ServiceException("请选择操作类型！");
//        }
        if (Objects.isNull(addWalletBo.getUserId())) {
            throw new ServiceException("请选择某一用户进行操作！");
        }
        if (StringUtils.isEmpty(addWalletBo.getRemark())) {
            throw new ServiceException("操作备注不能为空！");
        }
        if (Objects.isNull(addWalletBo.getGold())) {
            addWalletBo.setGold(0);
        }
        if (Objects.isNull(addWalletBo.getDiamond())) {
            addWalletBo.setDiamond(0);
        }
        if (addWalletBo.getGold() == 0 && addWalletBo.getDiamond() == 0) {
            throw new ServiceException("请至少选择金币或钻石任意一项进行操作！");
        }
        PayWalletLog log = BeanUtil.toBean(addWalletBo, PayWalletLog.class);
        // 加金币
        PayLogDTO dto = PayLogDTO.builder()
            .traceId(log.getId())
            .uid(log.getUserId())
            .changeNum(BigDecimal.valueOf(log.getGold().longValue()))
            .logType(PayLogTypeEnums.收益_手动添加用户余额)
            .build();
        int result = 0;
        if (addWalletBo.getGold() > 0) {
            dto.setServiceTag("手动添加用户金币:" + log.getId());
            result = appUserMapper.logForAddGoldBalance(dto);
            if (result != 1) {
                throw new ServiceException("操作失败！");
            }
        } else if (addWalletBo.getGold() < 0) {
            dto.setChangeNum(dto.getChangeNum().abs());
            dto.setServiceTag("手动扣除用户金币:" + log.getId());
            dto.setLogType(PayLogTypeEnums.消费_手动扣除用户钱包余额);
            result = appUserMapper.logSubGoldBalance(dto);
            if (result != 1) {
                throw new ServiceException("操作失败！");
            }
        }
        // 加钻石
        dto.setChangeNum(BigDecimal.valueOf(log.getDiamond().longValue()));
        if (addWalletBo.getDiamond() > 0) {
            dto.setServiceTag("手动添加用户钻石:" + log.getId());
            result = appUserMapper.logAddDiamondBalance(dto);
            if (result != 1) {
                throw new ServiceException("操作失败！");
            }
        } else if (addWalletBo.getDiamond() < 0) {
            dto.setChangeNum(dto.getChangeNum().abs());
            dto.setServiceTag("手动扣除用户钻石:" + log.getId());
            dto.setLogType(PayLogTypeEnums.消费_手动扣除用户钱包余额);
            result = appUserMapper.logSubDiamondBalance(dto);
            if (result != 1) {
                throw new ServiceException("操作失败！");
            }
        }
        walletLogMapper.insert(log);
    }

    @Override
    public TableDataInfo<PayWalletLogVo> queryPayWalletLog(Long userId, PageQuery query) {
        return TableDataInfo.build(
            (IPage<PayWalletLogVo>) walletLogMapper.selectVoPage(
                query.build(),
                Wrappers.lambdaQuery(PayWalletLog.class)
                    .eq(PayWalletLog::getUserId, userId)
                    .orderByDesc(PayWalletLog::getCreateTime)));
    }

    private void insertVipLog(AddVipBo vip, Date beforeTime, Date afterTime) {
        // 插入支付订单
        List<PayDetail> list = payDetailMapper.selectList(Wrappers.lambdaQuery(PayDetail.class)
            .eq(PayDetail::getUserId, vip.getId())
            .orderByDesc(PayDetail::getCreateTime));
        PayDetail detail;
        if (null == list || list.isEmpty()) {
            detail = PayDetail.builder().build();
            detail.setPlatformId(appUserMapper.selectById(vip.getId()).getPlatformId());
            detail.setUserId(vip.getId());
        } else {
            detail = list.get(0);
        }
        detail.setId(null);
        detail.setPlanSnap("{}");
        detail.setCnyPrice(BigDecimal.ZERO);
        detail.setOrderId("手动添加会员_" + DateUtils.dateTimeNow("yyyyMMddHHmmssSSS"));
        detail.setStatus(PayStatusEnum.已支付.getCode());
        detail.setPayType(PayTypeEnum.后台手动添加.getCode());
        /** 充值类型1=金币2=会员 */
        detail.setRechargeType(2);
        detail.setMonth(vip.getExpireTime() / 30);
        detail.setBeforeExpireTime(beforeTime);
        detail.setAfterExpireTime(afterTime);
        detail.setThirdId(null);
        detail.setCreateBy(null);
        detail.setCreateTime(null);
        detail.setUpdateBy(null);
        detail.setUpdateTime(null);
        payDetailMapper.insert(detail);

        AppUserMapper.Wallect wallect = appUserMapper.queryBanlance(detail.getUserId());
        // 插入支付日志
        SpringUtil.publishEvent(
            PayLogEvent.builder()
                .walletSnapAfter(wallect.toString())
                .walletSnapBefore(wallect.toString())
                .logDate(DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN))
                .userId(detail.getUserId())
                .logType(PayLogTypeEnums.收益_手动添加VIP.getCode())
                .symbol("+")
                .balanceType(BalanceTypeEnums.金币.getCode())
                .remark(PayLogTypeEnums.收益_手动添加VIP.name())
                .changeNum(BigDecimal.ZERO)
                .build());


        PushMessage.sendSysNotify(appUserMapper.selectById(vip.getId()).getPlatformId(), "恭喜您，充值成功!您充值的会员，已到账，请前往个人中心查看!");
    }

    @Override
    public AppUserVip getVipInfo(Long id) {
        return appUserVipMapper.selectById(id);
    }

    @Override
    public void changePhone(PhoneLog log) {
        AppUser appUser = appUserMapper.selectOne(Wrappers.lambdaQuery(AppUser.class)
            .ne(AppUser::getStatus, AppUserStatus.永久删除.getCode())
            .eq(AppUser::getPhone, log.getPhone()));
        if (appUser != null) {
            throw new ServiceException("修改失败：手机号码重复！");
        }
        AppUserBo bo = new AppUserBo();
        bo.setId(log.getId());
        bo.setPhone(log.getPhone());
        log.setId(null);
        phoneLogService.insert(log);
        this.updateByBo(bo);
        // 强制踢下线
        this.kickOnline(bo.getId(), "当前用户手机号码已被修改，请重新登陆。");
    }

    @Override
    public TableDataInfo<PhoneLogVo> getPhoneLog(PhoneLog log, PageQuery query) {
        return phoneLogService.queryPageList(log, query);
    }

    private final IBrokerUserService sysUserService;


    @Override
    public TableDataInfo<IndexVo> queryRegisterCount(MonthStatisticsBo bo, PageQuery query) {
        bo.setBranch(sysUserService.selectUserById(LoginHelper.getUserId()).getBranch());
        return TableDataInfo.build(appUserMapper.queryRegisterCount(bo.getStartDate(), bo.getEndDate(), bo.getBranch(), query.build()));
    }
}
